<?php
namespace App\Common\Server\Api\Gateway\ApiRequest;

use App\Common as com;
use App as nameApp;
use App\Common\Server\Api as nameApi;
use App\Common\Server\Api\Gateway as nameGateway;

class ApiSign extends nameApi\Base {

	/**
	 * 附加的签名数据
	 * @var array
	 */
	private array $appendSignData = [];

	/**
	 * 是否验证签名
	 * @var bool
	 */
	private bool $isCheckSign = true;

	/**
	 * ApiRouter constructor.
	 * @param nameGateway\ApiRequest $apiRequest api请求对象
	 * @throws
	 */
	public function __construct( nameGateway\ApiRequest $apiRequest ) {
		$this->goWeakReferencePool()->create( 'apiRequest', $apiRequest );
		parent::__construct();
	}

	/**
	 * 获取 apiRequest 对象
	 * @return nameGateway\ApiRequest
	 * @throws
	 */
	private function apiRequest(  ):nameGateway\ApiRequest {
		return $this->goWeakReferencePool()->get( 'apiRequest' );
	}

	/**
	 * 检查是否需要签名
	 * @param bool|null $isCheckSign 是否验证签名。[{bool}]设置 [null]获取
	 * @return bool [false]不需要 [true]需要
	 */
	public function isCheckSign( bool|null $isCheckSign = null ):bool {
		if( $isCheckSign !== null ){
			$this->isCheckSign = $isCheckSign;
		}
		//
		return $this->isCheckSign;
	}

	/**
	 * 增加附加签名数据
	 * @param string $name 键名
	 * @param mixed $value 键值
	 * @return static
	 */
	public function addAppendSignData( string $name, mixed $value ):static {
		$this->appendSignData[ $name ] = $value;
		//
		return $this;
	}

	/**
	 * 检查签名
	 * @return bool [false]错误 [true]正确
	 */
	public function checkSign():bool {
		// 检查是否一样
		return $this->apiRequest()->apiData()->getSign() === $this->buildSign();
	}

	/**
	 * 签名生成算法
	 * @return string 32位字符串
	 */
	public function buildSign(  ):string {
		// 消息签名。将accessToken（为空时不拼接该值）、timestamp、requestId、paramJson、secretKey（为空时不拼接该值）
		$signStr = $this->apiRequest()->apiData()->getAccessToken();
		$signStr .= $this->apiRequest()->apiData()->getTimestamp();
		$signStr .= $this->apiRequest()->apiData()->getRequestId();
		// @todo 时间关系，暂未实现api参数的签名验证
		// 取api参数
		/*
		if( $this->apiRequest()->isMethod( 'GET' ) ){
			$apiParam = $this->request->query();
		}else if( $this->apiRequest()->isMethod( 'POST' ) ){
			$apiParam = $this->request->post();
		}else{
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysAssertError );
		}
		*/
		//
		/*
		$signData = $this->apiRequest()->apiData()->get();
		if( isset( $signData['sign'] ) ) unset( $signData['sign'] );
		$signData = com\Lib\Fun\Array_::arrayMerges( $signData, $this->appendSignData );
		*/
		// 生成签名
		return md5( $signStr );
	}

	/**
	 * 签名生成算法
	 * @param array $signData API调用的请求参数集合的关联数组，不包含sign参数
	 * @return string 返回参数签名值
	 */
	private function getTrimSignData( array $signData ):string {
		$singleArray = $this->nestArrayToSingleArray( $signData );
		ksort( $singleArray );
		$waitSignArray = [];
		foreach( $singleArray as $singleKey => $singleValue ){
			$waitSignArray[] = "{$singleKey}={$singleValue}";
		}
		//
		return implode( '', $waitSignArray );
	}

	/**
	 * 多维数组转一维数组
	 * @param array $signData 待转数组
	 * @param string $arrayKeyPrefix [不需要传入数据，方法内部使用] 数组前缀
	 * @return array 转换后的数组
	 */
	private function nestArrayToSingleArray( array $signData, string $arrayKeyPrefix = '' ):array {
		$singleArray = [];
		foreach( $signData as $apiKey => $apiValue ){
			if( is_array( $apiValue ) ){
				$arrayKeyPrefixMid = $arrayKeyPrefix === '' ? "{$apiKey}_" : "{$arrayKeyPrefix}_{$apiKey}_";
				$singleArray = array_merge( $singleArray, $this->nestArrayToSingleArray( $apiValue, $arrayKeyPrefixMid ) );
			}else{
				$singleArray[ "{$arrayKeyPrefix}{$apiKey}" ] = $apiValue;
			}
		}
		//
		return $singleArray;
	}

}
